package com.hk.core.autoconfigure.data.r2dbc;

import com.hk.commons.util.SnowflakeUtils;
import com.hk.core.data.r2dbc.domain.AbstractSnowflakeIdPersistable;
import com.hk.core.data.r2dbc.repository.BaseR2dbcRepository;
import com.hk.core.data.r2dbc.repository.BaseR2dbcRepositoryFactoryBean;
import com.hk.core.data.r2dbc.repository.TenantR2dbcRepositoryFactoryBean;
import io.r2dbc.spi.ConnectionFactory;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.annotation.Order;
import org.springframework.data.r2dbc.config.EnableR2dbcAuditing;
import org.springframework.data.r2dbc.mapping.event.BeforeConvertCallback;
import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories;
import org.springframework.data.relational.core.sql.SqlIdentifier;
import org.springframework.r2dbc.connection.lookup.AbstractRoutingConnectionFactory;
import reactor.core.publisher.Mono;

/**
 * R2dbc 自动配置
 *
 * @author Kevin
 */
@AutoConfiguration
@EnableR2dbcAuditing
@EnableAspectJAutoProxy
@ConditionalOnClass(value = BaseR2dbcRepository.class)
public class R2dbcAutoConfiguration {

    /**
     * 如果只有单个数据库，使用多租户模式
     * 判断 {@link R2dbcMultiDataSourceProperties#dataSources} 的条件是否等于1，
     * 或spring上下文中的 {@link ConnectionFactory} 不是 {@link AbstractRoutingConnectionFactory}
     */
    @Configuration
    @ConditionalOnProperty(prefix = "hk.r2dbc.multi-ds", name = "enabled", havingValue = "false")
    @EnableR2dbcRepositories(basePackages = {"com.hk.**.repository.r2dbc"},
            repositoryFactoryBeanClass = TenantR2dbcRepositoryFactoryBean.class)
    static class SingleDataSourceR2dbcRepository {

    }

    /**
     * 如果有多个数据库了，{@link com.hk.core.data.r2dbc.annotations.TenantId} 没有意义，因为，都每个租户的数据分开了。
     * <p>
     * 判断 {@link R2dbcMultiDataSourceProperties#dataSources} 的条件是否大于1，或spring上下文中多个dataSource对象
     */
    @Configuration
    @ConditionalOnProperty(prefix = "hk.r2dbc.multi-ds", name = "enabled", havingValue = "true", matchIfMissing = true)
    @EnableR2dbcRepositories(basePackages = {"com.hk.**.repository.r2dbc"},
            repositoryFactoryBeanClass = BaseR2dbcRepositoryFactoryBean.class)
    static class MultiDataSourceR2dbcRepository {

    }

    /**
     * 保存时，如果没有设置id，则设置一个
     */
    @Bean
    @Order(value = 1)
    public BeforeConvertCallback<AbstractSnowflakeIdPersistable> r2dbcSnowflakeIdReactiveBeforeSaveCallback() {
        return new SnowflakeIdGenBeforeConvertCallback();
    }

    /**
     * 不要使用 lambda 表达式，会获取不到  BeforeConvertCallback 的泛型
     * <a href="https://www.jianshu.com/p/6fd69868d626" />
     *
     * @see EntityCallbackDiscoverer.CallbackRetriever#discoverEntityCallbacks(BeanFactory)  方法中 type.getGeneric(0)
     */
    private static class SnowflakeIdGenBeforeConvertCallback implements BeforeConvertCallback<AbstractSnowflakeIdPersistable> {

        @Override
        public Publisher<AbstractSnowflakeIdPersistable> onBeforeConvert(AbstractSnowflakeIdPersistable entity, SqlIdentifier table) {
            if (entity.isNew()) {
                entity.setId(SnowflakeUtils.getIdGenerator().generate());
            }
            return Mono.just(entity);
        }
    }
}
